﻿#include "stdafx.h"
#include "DwfParse.h"

#include <iostream>

#include "dwfcore/MIME.h"
#include "dwfcore/FileInputStream.h"
#include "dwfcore/StreamFileDescriptor.h"

#include "dwf/package/EPlotSection.h"
#include "dwf/package/ContentManager.h"
#include "dwf/package/SectionContentResource.h"
#include "dwf/package/writer/DWF6PackageWriter.h"
#include "dwf/package/Manifest.h"
#include "dwf/publisher/plot/Plot.h"
#include "dwf/publisher/impl/DWF6PackagePublisher.h"

#include "whiptk/whip_toolkit.h"
#include "whiptk/w2d_class_factory.h"

#include "fstream"

using namespace DWFCore;
using namespace DWFToolkit;

#define ERR_CHECK(X) if ( (X) != WT_Result::Success ) { WD_Assert( false ); return es; }
#define ERR_NULLCHECK(X) if ( (X) == NULL ) { WD_Assert( false ); return; }
#define WD_NULLCHECK(X) if ( (X) == WD_Null ) { WD_Assert( false ); return WT_Result::Out_Of_Memory_Error; }


Acad::ErrorStatus DwfParse::DrawDwf(const CString& dwfPath, AcDbObjectIdArray& entityIds)
{
	auto es = Acad::eInvalidInput;

	CString w2dPath = _T("McRebarDetailDemoTempFile.w2d");

	if (!ExtractW2d(dwfPath, w2dPath))
	{
		return es;
	}

	WT_Class_Factory* classFactory = new WT_W2D_Class_Factory();

	WT_File* whip_file = classFactory->Create_File();

	whip_file->set_file_mode(WT_File::File_Read);
	whip_file->set_filename((const WT_Unsigned_Integer16*)w2dPath.GetString());
	whip_file->heuristics().set_deferred_delete(WD_True);
	ERR_CHECK(whip_file->open());

	m_units = nullptr;

	//可绘制对象
	std::vector<WT_Drawable*> drawableObjects;

	WT_Result result;

	do {
		result = whip_file->process_next_object();

		WT_Object* pObj = whip_file->object();
		if ((result == WT_Result::Success || result == WT_Result::End_Of_DWF_Opcode_Found)
			&& pObj != NULL)
		{
			if (WT_Object::Units_ID == pObj->object_id())
			{
				m_units = (WT_Units*)pObj;
			}
			if (pObj->object_type() == WT_Object::Drawable)
			{
				drawableObjects.push_back((WT_Drawable*)pObj);
			}
		}

	} while (result == WT_Result::Success);

	ASSERT(m_transform != nullptr);

	if (m_units != nullptr)
	{
		auto transOriginPt = m_units->transform(WT_Logical_Point(0, 0));
		auto transPt = m_units->transform(WT_Logical_Point(100, 100));

		m_transform.m_translate.m_x = transOriginPt.m_x;
		m_transform.m_translate.m_y = transOriginPt.m_y;

		m_transform.m_x_scale = (transPt.m_x - transOriginPt.m_x) / 100;
		m_transform.m_y_scale = (transPt.m_y - transOriginPt.m_y) / 100;
	}

	//开始绘制
	for (auto obj : drawableObjects)
	{
		AcDbObjectId entityId;
		if (eOk == Draw(obj, entityId))
		{
			entityIds.append(entityId);
		}
	}

	whip_file->close();

	classFactory->Destroy(whip_file);

	delete classFactory;
	classFactory = nullptr;

	return eOk;
}

bool DwfParse::ExtractW2d(const CString& dwfPath, const CString& w2dPath)
{
	DWFFile oDWF(dwfPath.GetString());

	DWFPackageReader oReader(oDWF);
	DWFManifest& rManifest = oReader.getManifest();

	DWFSection* pSection = NULL;
	DWFManifest::SectionIterator* piSections = rManifest.getSections();

	bool success = false;

	if (piSections)
	{
		for (; piSections->valid(); piSections->next())
		{
			pSection = piSections->get();

			auto piResources = pSection->getResourcesByMIME();

			for (; piResources && piResources->valid(); piResources->next())
			{
				DWFResource* res = piResources->value();

				if (res->mime() == _T("application/x-w2d"))
				{
					auto ss = res->getInputStream();

					size_t readNum = 0;

					//写入png数据到文件
					std::ofstream fout(w2dPath, std::ios::binary);

					const size_t partNum = 1024;

					do
					{
						char pBuffer[partNum] = { 0 };
						readNum = ss->read(pBuffer, partNum);
						fout.write(pBuffer, readNum);

					} while (readNum == partNum);

					success = true;

					fout.close();
				}
			}

			DWFCORE_FREE_OBJECT(piResources);
		}

		DWFCORE_FREE_OBJECT(piSections);
	}

	return success;
}

Acad::ErrorStatus DwfParse::Draw(WT_Drawable* drawable, AcDbObjectId& entityId)
{
	switch (drawable->object_id())
	{
	case WT_Object::Text_ID:
		return DrawText((WT_Text*)drawable, entityId);
	case WT_Object::Polyline_ID:
		return DrawPolyline((WT_Point_Set*)(WT_Polyline*)drawable, entityId);
	case WT_Object::Polytriangle_ID:
		return DrawPolyline((WT_Point_Set*)(WT_Polytriangle*)drawable, entityId);
	case WT_Object::Outline_Ellipse_ID:
		return DrawEllipse((WT_Ellipse*)drawable, entityId);
	default:
		return Acad::eInvalidInput;
	}
}

Acad::ErrorStatus DwfParse::DrawText(WT_Text* wtText, AcDbObjectId& entityId)
{
	if (wtText->bounds().bounds() == nullptr)
	{
		return Acad::eInvalidInput;
	}

	auto lb = Transform(wtText->bounds().bounds()[0]);
	auto rt = Transform(wtText->bounds().bounds()[3]);
	auto position = Transform(wtText->position());

	double height = rt.m_y - lb.m_y;

	AcDbText* text = new AcDbText(AcGePoint3d(position.m_x, position.m_y, 0), (ACHAR*)wtText->string().unicode(), AcDbObjectId::kNull, height);

	//AcDbExtents ext;
	//text->getGeomExtents(ext);

	//double widthFactor = (rt.m_x - lb.m_x) / (ext.maxPoint().x - ext.minPoint().x);
	//text->setWidthFactor(widthFactor);

	auto es = m_database->AppendEntity(text, entityId);

	if (es == eOk)
	{
		text->close();
	}
	else
	{
		delete text;
	}

	return es;
}

Acad::ErrorStatus DwfParse::DrawPolyline(WT_Point_Set* wtPolyline, AcDbObjectId& entityId)
{
	AcDbPolyline* line = new AcDbPolyline();

	for (int i = 0; i < wtPolyline->count(); ++i)
	{
		AcGePoint3d pt(Transform(wtPolyline->points()[i]).m_x, Transform(wtPolyline->points()[i]).m_y, 0);

		line->addVertexAt(i, AcGePoint2d(pt.x, pt.y));
	}

	auto es = m_database->AppendEntity(line, entityId);

	if (es == eOk)
	{
		line->close();
	}
	else
	{
		delete line;
	}

	return es;
}

Acad::ErrorStatus DwfParse::DrawEllipse(WT_Ellipse* wtEllipse, AcDbObjectId& entityId)
{
	AcGePoint3d center(Transform(wtEllipse->position()).m_x, Transform(wtEllipse->position()).m_y, 0);

	double radiusRatio = (double)wtEllipse->minor() / wtEllipse->major();

	if (radiusRatio > 1 || radiusRatio <= 0)
	{
		return Acad::eInvalidInput;
	}

	double major = wtEllipse->major();

	major = major * m_transform.m_x_scale;

	if (major < 0)
		major = -major;

	double startAngle = WTAngle2Angle(wtEllipse->start());
	double endAngle = WTAngle2Angle(wtEllipse->end());
	double tiltAngle = WTAngle2Angle(wtEllipse->tilt());

	AcGeVector3d majorAxis(major, 0, 0);

	majorAxis.rotateBy(tiltAngle, AcGeVector3d::kZAxis);

	AcDbEllipse* ellipse = new AcDbEllipse(center,
		AcGeVector3d::kZAxis,
		majorAxis,
		radiusRatio,
		startAngle,
		endAngle
	);

	auto es = m_database->AppendEntity(ellipse, entityId);

	if (es == eOk)
	{
		ellipse->close();
	}
	else
	{
		delete ellipse;
	}

	return es;
}

WT_Point3D DwfParse::Transform(const WT_Logical_Point &point)
{
	if (m_units == nullptr)
	{
		return WT_Point3D(point.m_x, point.m_y);
	}
	else
	{
		return m_units->transform(point);
	}
}

double DwfParse::WTAngle2Angle(int wtAngle)
{
	return (double)wtAngle / 65536 * 2 * PI;
}